package com.leyou.trade.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.leyou.auth.utils.UserContext;
import com.leyou.common.exceptions.LyException;
import com.leyou.item.client.ItemClient;
import com.leyou.item.dto.SkuDTO;
import com.leyou.trade.dto.OrderFormDTO;
import com.leyou.trade.entity.Order;
import com.leyou.trade.entity.OrderDetail;
import com.leyou.trade.entity.OrderLogistics;
import com.leyou.trade.entity.enums.OrderStatus;
import com.leyou.trade.mapper.OrderMapper;
import com.leyou.trade.service.OrderDetailService;
import com.leyou.trade.service.OrderLogisticsService;
import com.leyou.trade.service.OrderService;
import com.leyou.trade.util.PayHelper;
import com.leyou.user.client.UserClient;
import com.leyou.user.dto.AddressDTO;
import feign.FeignException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

import static com.leyou.common.constants.MQConstants.ExchangeConstants.ORDER_EXCHANGE_NAME;
import static com.leyou.common.constants.MQConstants.RoutingKeyConstants.EVICT_ORDER_KEY;

/**
 * @author 虎哥
 */
@Slf4j
@Service
public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements OrderService {

    @Autowired
    private ItemClient itemClient;
    @Autowired
    private OrderDetailService detailService;
    @Autowired
    private OrderLogisticsService logisticsService;
    @Autowired
    private UserClient userClient;
    @Autowired
    private PayHelper payHelper;

    @Autowired
    private AmqpTemplate amqpTemplate;

    @Transactional
    @Override
    public Long createOrder(OrderFormDTO orderFormDTO) {
        // 1.处理order数据
        Order order = new Order();
        // 1.1.处理订单金额相关数据，查询sku、根据sku价格计算总金额、根据物流算运费、根据商品算优惠、计算实付金额
        // 1.1.1.获取订单中的商品基本信息
        Map<Long, Integer> carts = orderFormDTO.getCarts();
        // 1.1.2.获取sku的id
        Set<Long> skuIds = carts.keySet();
        // 1.1.3.查询sku
        List<SkuDTO> skuList = itemClient.querySkuByIds(skuIds);
        // 1.1.4.计算总金额
        long total = 0;
        for (SkuDTO sku : skuList) {
            // 判断商品状态
            if (!sku.getSaleable()) {
                // 商品下架
                throw new LyException(400, "商品已经下架，无法下单");
            }
            // 获取商品的购买数量
            Integer num = carts.get(sku.getId());
            total += sku.getPrice() * num;
        }
        order.setTotalFee(total);
        order.setPaymentType(orderFormDTO.getPaymentType());
        // TODO 1.1.5.根据物流算运费，目前全场包邮
        order.setPostFee(0L);
        // TODO 根据商品算优惠，目前没有优惠
        order.setActualFee(total + order.getPostFee());

        // 1.2.用户信息
        Long userId = UserContext.getUser().getId();
        order.setUserId(userId);

        // 1.3.默认状态
        order.setStatus(OrderStatus.INIT.getValue());

        // 1.4.写入数据库
        boolean success = save(order);
        if (!success) {
            // 订单写入失败
            throw new LyException(500, "订单新增失败！");
        }
        // 1.5.id回显
        Long orderId = order.getOrderId();

        // 2.处理 OrderDetail
        List<OrderDetail> details = new ArrayList<>(skuIds.size());
        // 2.1.将每一个sku，转为一个OrderDetail
        for (SkuDTO sku : skuList) {
            OrderDetail detail = new OrderDetail();
            details.add(detail);
            detail.setTitle(sku.getTitle());
            detail.setSpec(sku.getSpecialSpec());
            detail.setSkuId(sku.getId());
            detail.setPrice(sku.getPrice());
            detail.setOrderId(orderId);
            detail.setNum(carts.get(sku.getId()));
            detail.setImage(StringUtils.substringBefore(sku.getImages(), ","));
        }
        // 2.3.写入数据库
        detailService.saveBatch(details);

        // 3.处理OrderLogistics
        Long addressId = orderFormDTO.getAddressId();
        // 3.1.先根据id查询地址信息
        AddressDTO addressDTO = userClient.queryAddressById(addressId);
        // 3.2.查询的地址信息封装到OrderLogistics
        OrderLogistics orderLogistics = addressDTO.toEntity(OrderLogistics.class);
        orderLogistics.setOrderId(orderId);
        // 3.3.写入数据库
        success = logisticsService.save(orderLogistics);
        if (!success) {
            throw new LyException(500, "订单新增失败！");
        }

        // 4.减库存
        try {
            itemClient.deductStock(carts);
        } catch (FeignException e) {
            throw new LyException(e.status(), e.contentUTF8());
        }

        // TODO 5.删除购物车中已经下单的商品

        // 6.发送MQ消息到延迟队列
        amqpTemplate.convertAndSend(ORDER_EXCHANGE_NAME, EVICT_ORDER_KEY, orderId);
        return orderId;
    }

    @Override
    public String getPayUrl(Long orderId) {
        // 1.查询订单
        Order order = getById(orderId);
        // 2.是否为空
        if (order == null) {
            // 订单不存在
            throw new LyException(400, "订单编号不存在");
        }
        // 3.判断订单状态
        Integer status = order.getStatus();
        if (!Objects.equals(OrderStatus.INIT.getValue(), status)) {
            // 订单已经支付或关闭，无需再次生成链接
            throw new LyException(400, "订单已经支付或关闭");
        }
        // 4.生成支付链接
        return payHelper.getPayUrl(orderId, order.getActualFee());
    }

    @Override
    public void handleNotify(Map<String, String> data) {
        // 1.校验通信标示
        payHelper.checkReturnCode(data);
        // 2.校验业务标示
        payHelper.checkResultCode(data);
        // 3.校验签名
        payHelper.isSignatureValid(data);
        // 4.订单金额校验
        // 4.1.获取结果中的订单id和支付金额
        String outTradeNo = data.get("out_trade_no");
        String totalFee = data.get("total_fee");
        // 4.2.校验数据是否为空
        if (StringUtils.isBlank(outTradeNo) || StringUtils.isBlank(totalFee)) {
            // 回调结果参数有问题
            throw new LyException(400, "通知参数有误！");
        }
        Long orderId = null;
        try {
            // 4.3.查询数据库订单金额
            orderId = Long.valueOf(outTradeNo);
        } catch (NumberFormatException e) {
            // 数据有误
            throw new LyException(400, "通知参数有误！");
        }
        Order order = getById(orderId);
        if (order == null) {
            // 订单id是错误的
            throw new LyException(400, "订单id错误");
        }
        // 4.4.校验订单金额
        Long actualFee = order.getActualFee();
        if (!StringUtils.equals(actualFee.toString(), totalFee)) {
            // 订单金额不匹配
            throw new LyException(400, "订单金额错误");
        }

        // 5.业务幂等校验(判断订单状态）
        if (!Objects.equals(order.getStatus(), OrderStatus.INIT.getValue())) {
            // 订单已经支付或者关闭，是重复通知，无需处理
            return;
        }

        // 6.修改订单状态从未支付到已支付
        // update tb_order set status = 2, pay_time = NOW() where order_id = 110 AND status = 1
        update()
                .set("status", OrderStatus.PAY_UP.getValue())
                .set("pay_time", new Date())
                .eq("order_id", orderId)
                .eq("status", OrderStatus.INIT.getValue())
                .update();

        log.info("接收并处理了微信回调通知，更新订单状态成功！订单id：{}", orderId);
    }

    @Override
    public Integer queryOrderStatus(Long orderId) {
        // 1.查询订单
        Order order = getById(orderId);
        if (order == null) {
            // 订单不存在
            throw new LyException(400, "订单不存在！");
        }
        // 2.返回状态
        return order.getStatus();
    }

    @Override
    @Transactional
    public void evictOverdueOrder(Long orderId) {
        // 1.查询订单
        Order order = getById(orderId);
        if (order == null) {
            // 订单不存在
            log.debug("要清理的订单{}不存在！", orderId);
            return;
        }
        // 2.判断订单状态
        if (!order.getStatus().equals(OrderStatus.INIT.getValue())) {
            // 已经支付或关闭
            log.debug("订单{}已经支付或关闭，无需清理！", orderId);
            return;
        }
        // 3.关闭订单，做好幂等处理
        // update tb_order set status = 5, close_time = NOW() where order_id = 1 AND status = 1
        boolean success = update().set("status", OrderStatus.CLOSED.getValue())
                .set("close_time", new Date())
                .eq("order_id", orderId)
                .eq("status", OrderStatus.INIT.getValue())
                .update();
        if (!success) {
            // 失败了，有可能是重复请求
            return;
        }
        // 4.查询订单详情
        List<OrderDetail> detailList = detailService.query().eq("order_id", orderId).list();
        // 5.获取购买的商品及数量信息
        /*Map<Long, Integer> skuMap = new HashMap<>(detailList.size());
        for (OrderDetail detail : detailList) {
            skuMap.put(detail.getSkuId(), detail.getNum());
        }*/
        Map<Long, Integer> map = detailList.stream()
                .collect(Collectors.toMap(OrderDetail::getSkuId, OrderDetail::getNum));
        // 6.恢复库存
        try {
            itemClient.plusStock(map);
        } catch (FeignException e) {
            throw new LyException(e.status(), e.contentUTF8());
        }
    }
}